package com.cm.kits;

import com.cm.kits.exceptions.PackingException;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.TimeZone;

/**
 * Jackson 序列化反序列化工具
 *
 * @author LWang 2023.01.18
 * @since 1.0.0
 */
public final class JacksonKits {
    private JacksonKits() {

    }


    private static volatile ObjectMapper OBJECT_MAPPER;

    /**
     * 将对象转换为 Json 字符串
     *
     * @param source 要执行转换的对象
     * @return 序列化后的 Json 字符串
     */
    public static String toJson(Object source) {
        if (null == source) {
            // null 对象，返回空的 json 对象
            return "{}";
        }
        try {
            return getObjectMapper().writeValueAsString(source);
        } catch (JsonProcessingException e) {
            throw new PackingException(e);
        }
    }

    /**
     * 将 Json 字符串转为对象
     *
     * @param content 要执行反序列化的 Json 字符串
     * @param cls     序列化结果类
     * @param <T>     结果类泛型
     * @return 反序列化结果
     */
    public static <T> T toBean(String content, Class<T> cls) {
        if (StringUtils.isBlank(content) || cls == null) {
            return null;
        }
        ObjectMapper objectMapper = getObjectMapper();
        try {
            return objectMapper.readValue(content, cls);
        } catch (JsonProcessingException e) {
            throw new PackingException(e);
        }
    }

    /**
     * @param content 要执行反序列化的 Json 字符串
     * @param ref     序列化结果类型
     * @param <T>     结果类泛型
     * @return 反序列化结果
     */
    public static <T> T toBean(String content, TypeReference<T> ref) {
        if (StringUtils.isBlank(content) || ref == null) {
            return null;
        }
        ObjectMapper objectMapper = getObjectMapper();
        try {
            return objectMapper.readValue(content, ref);
        } catch (JsonProcessingException e) {
            throw new PackingException(e);
        }
    }

    /**
     * 将 Json 字符串转为对象列表
     *
     * @param content 要执行反序列化的 Json 字符串
     * @param ref     序列化结果类型
     * @param <T>     结果类泛型
     * @return 反序列化结果
     */
    public static <T> List<T> toList(String content, TypeReference<List<T>> ref) {
        if (StringUtils.isBlank(content) || ref == null) {
            return null;
        }
        ObjectMapper objectMapper = getObjectMapper();
        try {
            return objectMapper.readValue(content, ref);
        } catch (JsonProcessingException e) {
            throw new PackingException(e);
        }
    }

    /**
     * 将 Json 字符串转为 JsonNode 对象，适用于处理无需建立 JavaBean 的临时 Json 字符串处理
     *
     * @param content 要执行反序列化的 Json 字符串
     * @return 反序列化结果
     */
    public static JsonNode toJsonNode(String content) {
        try {
            return getObjectMapper().readTree(content);
        } catch (JsonProcessingException e) {
            throw new PackingException(e);
        }
    }

    /**
     * 获取一个 ObjectMapper 对象
     *
     * @return ObjectMapper 对象
     */
    public static ObjectMapper getObjectMapper() {
        if (OBJECT_MAPPER == null) {
            synchronized (JacksonKits.class) {
                if (OBJECT_MAPPER == null) {
                    ObjectMapper objectMapper = new ObjectMapper();
                    // 设置时区
                    objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
                    // 设置时间格式
                    objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
                    JavaTimeModule javaTimeModule = new JavaTimeModule();
                    /*
                     * 序列化配置,针对java8 时间
                     */
                    javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                    javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                    javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

                    /*
                     * 反序列化配置,针对java8 时间
                     */
                    javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                    javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                    javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

                    /*
                     * 声明自定义模块,配置double类型序列化配置
                     */
                    SimpleModule module = new SimpleModule("DoubleSerializer", new Version(1, 0, 0, "", "", ""));
                    // 注意Double和double需要分配配置
                    JsonSerializer<Double> serializer = new JsonSerializer<>() {
                        @Override
                        public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers)
                                throws IOException {
                            BigDecimal d = BigDecimal.valueOf(value);
                            gen.writeNumber(d.stripTrailingZeros().toPlainString());
                        }

                        @Override
                        public Class<Double> handledType() {
                            return Double.class;
                        }
                    };
                    module.addSerializer(Double.class, serializer);
                    module.addSerializer(double.class, serializer);

                    /*
                     * 注册模块
                     */
                    objectMapper
                            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                            .registerModule(javaTimeModule)
                            .registerModule(module)
                            .registerModule(new Jdk8Module())
                            .registerModule(new ParameterNamesModule())
                            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                            .setSerializationInclusion(JsonInclude.Include.NON_NULL);
                    OBJECT_MAPPER = objectMapper;
                }
            }
        }
        return OBJECT_MAPPER;
    }

}
